\documentclass[12pt]{report}
\usepackage{geometry}
\geometry{a4paper,scale=0.8}
\usepackage{longtable}
\usepackage{url}
\usepackage{listings}
\usepackage{graphicx}
\usepackage{float}
\usepackage{titlesec}
\usepackage{CJK}
\begin{CJK}{UTF8}{gkai}
%\titleformat{\chapter}[hang]{\centering\Huge\bfseries}{\chaptername}{1em}{}
%\renewcommand{\chaptername}{第\CJKnumber{thechapter}章}
\title{A002 学习linux0.01的控制台输出}
\author{Chuzy}
\date{2020/05/03}

\lstset{
    basicstyle          =   \small\ttfamily,          % 基本代码风格
    keywordstyle        =   \bfseries,          % 关键字风格
    commentstyle        =   \rmfamily\itshape,  % 注释的风格，斜体
    stringstyle         =   \ttfamily,  % 字符串风格
    flexiblecolumns,                % 别问为什么，加上这个
    numbers             =   left,   % 行号的位置在左边
    showspaces          =   false,  % 是否显示空格，显示了有点乱，所以不现实了
    numberstyle         =   \ttfamily,    % 行号的样式，小五号，tt等宽字体
    showstringspaces    =   false,
    captionpos          =   t,      % 这段代码的名字所呈现的位置，t指的是top上面
    frame               =   lrtb,   % 显示边框
    columns         =   flexible,     
    breaklines      =   true,
    language        =  [ANSI]C,
    escapeinside=``
}


%以上部分叫做"导言区",下面才开始写正文
\begin{document}

%先插入标题
\maketitle
\tableofcontents
\newpage 
\chapter{概述}
在001中，我们用16位汇编代码boot.s, 32位汇编代码head.s和c代码混编，最后把控制权交给了main函数。在002中，在作为向前迈进的第一步，我们先尝试在屏幕上打印一个Hello World出来。
\begin{figure}[htbp]
\centering
\includegraphics[scale=0.5]{fig/34.png}
\caption{0.002版本运行结果}
\label{fig:Version 0.002}
\end{figure}

\chapter{流程图}
\makeatletter\def\@captype{table}\makeatother
\caption{0.002版本的文件}  
\begin{center}  
\begin{tabular}{|l|l|p{5cm}|p{3cm}|}  
\hline 目录 & 文件名 & 描述 & 相对上一版本\\
\hline  / & Makefile & 主make文件 & 变化\\ 
\hline  / & tmp.sh & 填充system长度 & 不变\\ 
\hline  /boot & boot.s & 引导扇区 & 不变\\ 
\hline  /boot & head.s & 基础内核 & 不变\\ 
\hline  /init & main.c & 高层初始化 & 变化\\
\hline  /kernel & console.c & con处理 & 增加\\
\hline  /kernel & printk.c & 内核打印 & 增加\\
\hline  /kernel & vsprintf.c & 格式化输出 & 增加\\
\hline  /kernel & sched.c & 用户堆栈定义 & 增加\\
\hline  /kernel & tty\_io.c & 字符终端输入输出 & 增加\\
\hline  /kernel & Makefile & 内核目录下的make文件 & 增加\\
\hline  /include & stdarg.h & 可变长的参数 & 增加\\
\hline  /include & stddef.h & 标准类型定义 & 增加\\
\hline  /include & string.h & 字符串处理汇编 & 增加\\
\hline  /include & termios.h & 终端输入输出 & 增加\\
\hline  /include/asm & io.h & 端口控制的汇编 & 增加\\
\hline  /include/asm & segment.h & 段寄存器处理 & 增加\\
\hline  /include/asm & system.h & 系统类的汇编代码 & 增加\\
\hline  /include/linux & tty.h & 电传打字输入输出头 & 增加\\
\hline  /include/sys & types.h & 类型定义头 & 增加\\
\hline  /tools & build.c & 内核Image生成工具 & 不变\\
\hline  
\end{tabular}  
\end{center}  

\paragraph{} 我们的目标是为内核增加打印功能，首先需要初始化控制台终端，其过程是：main-$>$tty\_init-$>$con\_init. 初始化完成之后，我们直接在main函数中发起打印：main-$>$printk-$>$tty\_write-$>$con\_wirte， 实现在控制台终端的输出。 解释一下两个基本概念：
\paragraph{终端} 终端指输入输出设备，本身不提供运算处理功能。早期的终端一般是电传打字机 （tty），比如ASR33。后来出现了视频终端，典型的终端使用RS-232之类的串行数据通信与主机相连，后来智能终端（如VT52和VT100）被引入。它们能理解转义序列，可以定位光标和控制显示位置。现在由于计算机硬件越来越便宜，通常都是一个人独占一台计算机，不再连接以前那种真正意义上的“终端设备”。在linux中，用alt+f1~f6 可以切换六个虚拟终端，就好比是以前多人公用的计算机中的六个终端设备。
\paragraph{控制台终端} 直接连接在电脑上的键盘和显示器叫做控制台。终端是通过串口连接上的，不是计算机本身就有的设备，而控制台是。控制台也有终端一样的功能，有时候也被模糊的统称为终端。计算机操作系统中，与终端不相关的信息，比如内核消息，后台服务消息，都可以显示到控制台上，但不会显示到终端上。linux中已经完全淡化了控制台和终端的区别，也就是说，linux把当前的终端当作控制台来看待。但是在其他的UNIX类系统中，却很明显的有虚拟终端和控制台的区别。比如freeBSD系统。 

\chapter{初始化流程控制源码init/main.c}
当控制权从head.s交给main.c之后，main函数中就要开始进行各种设备的初始化。当所有初始化完成之后，main函数会启动shell，在shell中等待用户进行下一步的操作。
\section{main函数}
现在，我们只需要在控制台终端上打印出一个Hello World，我们就只用初始化tty，并调用内核打印函数。
\begin{lstlisting}[breaklines]
#include <linux/tty.h>
void main(void)
{
	tty_init();
	printk("Hello World!");
	for(;;);
}
\end{lstlisting}
\chapter{tty操作源码kernel/tty\_io.c}
在linux0.01中支持3种tty，tty0对应控制台终端，tty1和tty2对应串口终端。我们现在只需要考虑tty0控制台终端。
\begin{lstlisting}
#include <linux/tty.h>

struct tty_struct tty_table[] = {
	{
		{0,		/* `输入, CR转NL, 回车转换行` */
		OPOST|ONLCR,	/* change outgoing NL to CRNL */
		0,		/* `控制模式标志, 如果是串口，对应速率和位宽` */
		ICANON | ECHO | ECHOCTL | ECHOKE,	/* `本地模式标志` */
			/* `规范/熟模式，回显输入, 回显控制信号，KILL删除行上所有字符` */
		0,		/* console termio `tty都是0` */
		INIT_C_CC},	/* `控制字符数组` */
		0,			/* initial pgrp `所属的初始进程组`*/
		0,			/* initial stopped `初始停止标志`*/
		con_write,
		{0,0,0,0,""},		/* console read-queue */
					/* data, head, tail, proc_list, buffer */
		{0,0,0,0,""},		/* console write-queue */
		{0,0,0,0,""}		/* console secondary queue */
	}
};
\end{lstlisting}
\section{tty\_init函数}
根据tty的类型，直接调用控制台终端的init函数。
\begin{lstlisting}
void tty_init(void)
{
	con_init();
}
\end{lstlisting}
\section{tty\_write函数}
把buffer里面的nr个字符存入tty的write队列，然后调用tty的wirte函数输出。通过这样的处理，将不同类型tty的接口归一化。
\begin{lstlisting}
int tty_write(unsigned channel, char * buf, int nr)
{
	struct tty_struct * tty;
	char c;
        char *b = buf;
	tty = tty_table;
	while (nr>0) {
		c = *(b);
		b++; nr--;
		PUTCH(c,tty->write_q);
	}
	tty->write(tty);
	return 0;
}
\end{lstlisting}
\chapter{控制台操作源码kernel/console.c}
console.c包含了全部对显示器的处理和键盘的处理，我们现在只关心对显示器的处理。
\begin{lstlisting}
/*
 *	console.c
 *
 * This module implements the console io functions
 *	'void con_init(void)'
 *	'void con_write(struct tty_queue * queue)'
 * Hopefully this will be a rather complete VT102 implementation.
 *
 */

/*
 *  NOTE!!! We sometimes disable and enable interrupts for a short while
 * (to put a word in video IO), but this will work even for keyboard
 * interrupts. We know interrupts aren't enabled when getting a keyboard
 * interrupt, as we use trap-gates. Hopefully all is well.
 */

//#include <linux/sched.h>
#include <linux/tty.h>
#include <asm/io.h>
#include <asm/system.h>

#define SCREEN_START 0xb8000
#define SCREEN_END   0xc0000
#define LINES 25
#define COLUMNS 80
#define NPAR 16

//extern void keyboard_interrupt(void);

static unsigned long origin=SCREEN_START;
static unsigned long scr_end=SCREEN_START+LINES*COLUMNS*2;
static unsigned long pos;
static unsigned long x,y;
static unsigned long top=0,bottom=LINES;
unsigned long lines=LINES,columns=COLUMNS;
static unsigned long state=0;
static unsigned long npar,par[NPAR];
static unsigned long ques=0;
unsigned char attr=0x07;

/*
 * this is what the terminal answers to a ESC-Z or csi0c
 * query (= vt100 response).
 */
#define RESPONSE "\033[?1;2c"
\end{lstlisting}

\section{gotoxy函数}
移动光标位置到(x,y)。字符在屏幕上的位置通过(x,y)两个全局变量保存，(x,y)对应在显存中的位置保存在pos中，当前屏幕0点在显存中的位置保存在origin中。
\begin{lstlisting}
static inline void gotoxy(unsigned int new_x,unsigned int new_y)
{
	if (new_x>=columns || new_y>=lines)
		return;
	x=new_x;
	y=new_y;
	pos=origin+((y*columns+x)<<1);
}
\end{lstlisting}
\section{lf和ri函数}
set\_origin 让设置的新的origin生效。set\_origin 被scrup调用，scrup又被lf调用；lf作为正常的换行，引起的翻页更频繁，通过set\_origin 可以减少不必要的内存拷贝，提高效率。
\begin{lstlisting}

static inline void set_origin(void)
{
	cli();
	outb_p(12,0x3d4); 	/* `向显示寄存器端口写12, 即选择显示控制数据寄存器r12` */
	outb_p(0xff&((origin-SCREEN_START)>>9),0x3d5); 	/* `高字节写入数据端口` */
	outb_p(13,0x3d4); 	/* `向显示寄存器端口写13, 即选择显示控制数据寄存器r13` */
	outb_p(0xff&((origin-SCREEN_START)>>1),0x3d5); 	/* `低字节写入数据端口` */
	sti();
}
\end{lstlisting}
scrup被lf调用，正常情况下，是整个屏幕向上滚动，且不会超过SCREEN\_END 边界，这时只用设置origin，最下面用空行覆盖最后一行，不需要进行额外的内存拷贝；如果不幸超过了SCREEN\_END 边界，则将屏幕内容复制到SCREEN\_START ；如果不是整个屏幕向上滚动，则需要按需复制内存。
\begin{lstlisting}
static void scrup(void)
{
	if (!top && bottom==lines) {		/* `正常情况下成立`*/
		origin += columns<<1;
		pos += columns<<1;
		scr_end += columns<<1;
		if (scr_end>SCREEN_END) {	/* `从b8000到c00000, 一般不需进入`*/
			__asm__("cld\n\t"
				"rep\n\t"
				"movsl\n\t"	/* `从origin复制到SCREEN\_START`*/
				"movl columns,%1\n\t"
				"rep\n\t"
				"stosw"		/* `屏幕最下方空行`*/
				::"a" (0x0720),
				"c" ((lines-1)*columns>>1),
				"D" (SCREEN_START),
				"S" (origin)
				);
			scr_end -= origin-SCREEN_START;
			pos -= origin-SCREEN_START;
			origin = SCREEN_START;
		} else {
			__asm__("cld\n\t"
				"rep\n\t"
				"stosl"		/* `大多数情况下只需要填充一行`*/
				::"a" (0x07200720),
				"c" (columns>>1),
				"D" (scr_end-(columns<<1))
				);
		}
		set_origin();			/* `设置新的origin起点`*/
	} else {
		__asm__("cld\n\t"
			"rep\n\t"		/* `如果有改动top或bottom则无法全屏移动`*/
			"movsl\n\t"		/* `需要通过复制逐行覆盖`*/
			"movl columns,%%ecx\n\t"
			"rep\n\t"
			"stosw"
			::"a" (0x0720),
			"c" ((bottom-top-1)*columns>>1),
			"D" (origin+(columns<<1)*top),
			"S" (origin+(columns<<1)*(top+1))
			);
	}
}
\end{lstlisting}
被ri调用，屏幕向下滚动一行，即用上面一行的内容覆盖下面一行，最上面用空行覆盖第一行，形成一个新行。
\begin{lstlisting}
static void scrdown(void)
{
	__asm__("std\n\t"
		"rep\n\t"
		"movsl\n\t"		/* `从esi到edi的4字节覆盖`*/
		"addl $2,%%edi\n\t"	/* %edi has been decremented by 4, `补回2字节` */
		"movl columns,%%ecx\n\t"
		"rep\n\t"		/* `用ax的内容填充一行` */
		"stosw"			/* `2字节填充` */
		::"a" (0x0720),		/* `0x07属性，0x20 Ascii码空格` */
		"c" ((bottom-top-1)*columns>>1),	/* `以4字节为单位覆盖，除以2`*/
		"D" (origin+(columns<<1)*bottom-4),
		"S" (origin+(columns<<1)*(bottom-1)-4)
		);
}
\end{lstlisting}
lf即line feed，换行，光标移动到下一行，如果已经到达屏幕最下面一行，则需要将屏幕向上滚动一行。
\begin{lstlisting}
static void lf(void)
{
	if (y+1<bottom) {
		y++;
		pos += columns<<1;
		return;
	}
	scrup();
}
\end{lstlisting}
ri即reverse index，回到上一行，如果已经到达屏幕最上面一行，则需要将屏幕向下滚动一行。
\begin{lstlisting}
static void ri(void)
{
	if (y>top) {
		y--;
		pos -= columns<<1;
		return;
	}
	scrdown();
}
\end{lstlisting}
\section{移动/显示光标，插入/删除字符/行函数}
\begin{lstlisting}
static void cr(void)
{
	pos -= x<<1;
	x=0;
}

static void del(void)
{
	if (x) {
		pos -= 2;
		x--;
		*(unsigned short *)pos = 0x0720;
	}
}

static void csi_J(int par)
{
	long count __asm__("cx");
	long start __asm__("di");

	switch (par) {
		case 0:	/* erase from cursor to end of display */
			count = (scr_end-pos)>>1;
			start = pos;
			break;
		case 1:	/* erase from start to cursor */
			count = (pos-origin)>>1;
			start = origin;
			break;
		case 2: /* erase whole display */
			count = columns*lines;
			start = origin;
			break;
		default:
			return;
	}
	__asm__("cld\n\t"
		"rep\n\t"
		"stosw\n\t"
		::"c" (count),
		"D" (start),"a" (0x0720)
		);
}

static void csi_K(int par)
{
	long count __asm__("cx");
	long start __asm__("di");

	switch (par) {
		case 0:	/* erase from cursor to end of line */
			if (x>=columns)
				return;
			count = columns-x;
			start = pos;
			break;
		case 1:	/* erase from start of line to cursor */
			start = pos - (x<<1);
			count = (x<columns)?x:columns;
			break;
		case 2: /* erase whole line */
			start = pos - (x<<1);
			count = columns;
			break;
		default:
			return;
	}
	__asm__("cld\n\t"
		"rep\n\t"
		"stosw\n\t"
		::"c" (count),
		"D" (start),"a" (0x0720)
		);
}

void csi_m(void)
{
	int i;

	for (i=0;i<=npar;i++)
		switch (par[i]) {
			case 0:attr=0x07;break;
			case 1:attr=0x0f;break;
			case 4:attr=0x0f;break;
			case 7:attr=0x70;break;
			case 27:attr=0x07;break;
		}
}

static inline void set_cursor(void)
{
	cli();
	outb_p(14,0x3d4);
	outb_p(0xff&((pos-SCREEN_START)>>9),0x3d5);
	outb_p(15,0x3d4);
	outb_p(0xff&((pos-SCREEN_START)>>1),0x3d5);
	sti();
}
/*
static void respond(struct tty_struct * tty)
{
	char * p = RESPONSE;

	cli();
	while (*p) {
		PUTCH(*p,tty->read_q);
		p++;
	}
	sti();
	copy_to_cooked(tty);
}
*/
static void insert_char(void)
{
	int i=x;
	unsigned short tmp,old=0x0720;
	unsigned short * p = (unsigned short *) pos;

	while (i++<columns) {
		tmp=*p;
		*p=old;
		old=tmp;
		p++;
	}
}

static void insert_line(void)
{
	int oldtop,oldbottom;

	oldtop=top;
	oldbottom=bottom;
	top=y;
	bottom=lines;
	scrdown();
	top=oldtop;
	bottom=oldbottom;
}

static void delete_char(void)
{
	int i;
	unsigned short * p = (unsigned short *) pos;

	if (x>=columns)
		return;
	i = x;
	while (++i < columns) {
		*p = *(p+1);
		p++;
	}
	*p=0x0720;
}

static void delete_line(void)
{
	int oldtop,oldbottom;

	oldtop=top;
	oldbottom=bottom;
	top=y;
	bottom=lines;
	scrup();
	top=oldtop;
	bottom=oldbottom;
}

static void csi_at(int nr)
{
	if (nr>columns)
		nr=columns;
	else if (!nr)
		nr=1;
	while (nr--)
		insert_char();
}

static void csi_L(int nr)
{
	if (nr>lines)
		nr=lines;
	else if (!nr)
		nr=1;
	while (nr--)
		insert_line();
}

static void csi_P(int nr)
{
	if (nr>columns)
		nr=columns;
	else if (!nr)
		nr=1;
	while (nr--)
		delete_char();
}

static void csi_M(int nr)
{
	if (nr>lines)
		nr=lines;
	else if (!nr)
		nr=1;
	while (nr--)
		delete_line();
}

static int saved_x=0;
static int saved_y=0;

static void save_cur(void)
{
	saved_x=x;
	saved_y=y;
}

static void restore_cur(void)
{
	x=saved_x;
	y=saved_y;
	pos=origin+((y*columns+x)<<1);
}
\end{lstlisting}
\section{con\_write函数}
将缓存队列中的字符输出的屏幕上，函数的复杂度主要来自对于各种控制字符和转意序列的处理。
\begin{lstlisting}
void con_write(struct tty_struct * tty)
{
	int nr;
	char c;

	nr = CHARS(tty->write_q);
	while (nr--) {
		GETCH(tty->write_q,c);
		switch(state) {
			case 0:
				if (c>31 && c<127) {		/*`普通字符`*/
					if (x>=columns) {	/*`当前位置是否需要换行`*/
						x -= columns;
						pos -= columns<<1;
						lf();
					}			/*`屏幕输出`*/
					__asm__("movb attr,%%ah\n\t"
						"movw %%ax,%1\n\t"
						::"a" (c),"m" (*(short *)pos)
						);
					pos += 2;
					x++;			/*`下次输出的位置更新`*/
				} else if (c==27)	/*`遇到ESC，状态=1`*/
					state=1;
				else if (c==10 || c==11 || c==12)
					lf();		/*`垂直制表符，换行，换页，换行`*/
				else if (c==13)
					cr();		/*`回车`*/
//				else if (c==ERASE_CHAR(tty))
//					del();		/*`回退删除一个字符`*/
				else if (c==8) {
					if (x) {	/*`回退但不删除字符`*/
						x--;
						pos -= 2;
					}
				} else if (c==9) {
					c=8-(x&7);	/*`TAB键，向前移动到模8的位置`*/
					x += c;
					pos += c<<1;
					if (x>columns) {	/*`超过行的长度时，换行`*/
						x -= columns;
						pos -= columns<<1;
						lf();
					}
					c=9;
				}
				break;
			case 1:				/*`前一个字符是ESC转意时`*/
				state=0;		/*`恢复状态`*/
				if (c=='[')		/*`ESC+[，控制序列引导码，CSI`*/
					state=2;
				else if (c=='E')	/*`ESC+E，移动光标下一行0列`*/
					gotoxy(0,y+1);
				else if (c=='M')	/*`ESC+M，光标向上滚动一行`*/
					ri();
				else if (c=='D')	/*`ESC+D，光标向下滚动一行`*/
					lf();
//				else if (c=='Z')	/*`ESC+Z，发送终端应答`*/
//					respond(tty);
				else if (x=='7')	/*`ESC+7，保存光标位置`*/
					save_cur();
				else if (x=='8')
					restore_cur();	/*`ESC+8，恢复光标位置`*/
				break;
			case 2:		/*`前面2个字符是ESC+[，控制序列引导码，CSI`*/
				for(npar=0;npar<NPAR;npar++)
					par[npar]=0;
				npar=0;
				state=3;
				if (ques=(c=='?'))/*`如果不是?，转状态3处理`*/
					break;/*`?表示下个字符是私有控制字符,转3处理`*/
			case 3:
				if (c==';' && npar<NPAR-1) {
					npar++;/*`;表示一个参数结束`*/
					break;
				} else if (c>='0' && c<='9') {
					par[npar]=10*par[npar]+c-'0';
					break;/*`0-9的数字和之前的数字转换成10进制数`*/
				} else state=4;/*`其他则进入状态4处理`*/
			case 4:
				state=0;		/*`状态清0`*/
				switch(c) {	/*`CSI Pn G或转义符，水平移动到Pn`*/
					case 'G': case '``':
						if (par[0]) par[0]--;
						gotoxy(par[0],y);
						break;
					case 'A':	/*`CSI Pn A，向上移动Pn`*/
						if (!par[0]) par[0]++;
						gotoxy(x,y-par[0]);
						break;
					case 'B': case 'e':
						if (!par[0]) par[0]++;
						gotoxy(x,y+par[0]);
						break;	/*`CSI Pn B或e，向下移动Pn`*/
					case 'C': case 'a':
						if (!par[0]) par[0]++;
						gotoxy(x+par[0],y);
						break;	/*`CSI Pn C或a，向右移动Pn`*/
					case 'D':
						if (!par[0]) par[0]++;
						gotoxy(x-par[0],y);
						break;	/*`CSI Pn D，向左移动Pn`*/
					case 'E':
						if (!par[0]) par[0]++;
						gotoxy(0,y+par[0]);
						break;	/*`CSI Pn E，向下移动Pn, 回行首`*/
					case 'F':
						if (!par[0]) par[0]++;
						gotoxy(0,y-par[0]);
						break;	/*`CSI Pn F，向上移动Pn, 回行首`*/
					case 'd':
						if (par[0]) par[0]--;
						gotoxy(x,par[0]);
						break;	/*`CSI Pn d，垂直移动到Pn`*/
					case 'H': case 'f':
						if (par[0]) par[0]--;
						if (par[1]) par[1]--;
						gotoxy(par[1],par[0]);
						break;	/*`CSI Pn H或f，移动到P1/P2`*/
					case 'J':
						csi_J(par[0]);
						break;	/*`CSI Pn J，Pn作为参数清屏`*/
					case 'K':
						csi_K(par[0]);
						break;	/*`CSI Pn K，Pn作为参数清行`*/
					case 'L':
						csi_L(par[0]);
						break;	/*`CSI Pn L，插入Pn行`*/
					case 'M':
						csi_M(par[0]);
						break;	/*`CSI Pn M，删除Pn行`*/
					case 'P':
						csi_P(par[0]);
						break;	/*`CSI Pn P，删除Pn字符`*/
					case '@':
						csi_at(par[0]);
						break;	/*`CSI Pn @，插入Pn字符`*/
					case 'm':
						csi_m();
						break;	/*`CSI Pn m，设置显示属性Pn`*/
					case 'r':
						if (par[0]) par[0]--;
						if (!par[1]) par[1]=lines;
						if (par[0] < par[1] &&
						    par[1] <= lines) {
							top=par[0];
							bottom=par[1];
						}
						break;	/*`CSI Pn r，设置滚屏起止行号`*/
					case 's':
						save_cur();	/*`CSI s，保存光标位置`*/
						break;
					case 'u':
						restore_cur();
						break;	/*`CSI s，恢复光标位置`*/
				}
		}
	}
	set_cursor();	/*`在新的位置显示光标`*/
}
\end{lstlisting}
\section{con\_init函数}
在boot.s中，我们把屏幕光标的位置保存在了offset为0x510和0x511内存处，由于boot.s被加载在0x90000处，因此对应的实际地址是0x90510和0x90511。我们从这里直接获取光标位置。现在我们还不需要处理键盘响应，暂时先把初始化键盘中断的代码注释掉。
\begin{lstlisting}
/*
 *  void con_init(void);
 *
 * This routine initalizes console interrupts, and does nothing
 * else. If you want the screen to clear, call tty_write with
 * the appropriate escape-sequece.
 */
void con_init(void)
{
	register unsigned char a;

	gotoxy(*(unsigned char *)(0x90000+510),*(unsigned char *)(0x90000+511));
//	set_trap_gate(0x21,&keyboard_interrupt);
//	outb_p(inb_p(0x21)&0xfd,0x21);
//	a=inb_p(0x61);
//	outb_p(a|0x80,0x61);
//	outb(a,0x61);
}
\end{lstlisting}

\chapter{内核打印输出源码kernel/printk.c}
printk是内核态的打印输出函数，实质和标准的printf函数相同。存在这两个相同函数的原因是，正常情况下tty\_write 需要通过fs寄存器访问用户数据段，而在内核态不使用fs，因此内核打印时需要将fs置为ds，这样以来tty\_write 就不需要针对内核打印做特殊处理了。
\section{printk函数}
调用vsprintf对要打印的内容进行格式化处理，然后通过汇编代码将fs置为ds，调用tty\_write 。
\begin{lstlisting}
/*
 * When in kernel-mode, we cannot use printf, as fs is liable to
 * point to 'interesting' things. Make a printf with fs-saving, and
 * all is well.
 */
#include <stdarg.h>
#include <stddef.h>

//#include <linux/kernel.h>

static char buf[1024];

int printk(const char *fmt, ...)
{
	va_list args;
	int i;

	va_start(args, fmt);
	i=vsprintf(buf,fmt,args);
	va_end(args);
	__asm__("push %%fs\n\t"
		"push %%ds\n\t"
		"pop %%fs\n\t"		/*`FS=DS`*/
		"pushl %0\n\t"		/*`参数3: 缓冲区长度`*/
		"pushl $buf\n\t"	/*`参数2: buf，缓冲区地址`*/
		"pushl $0\n\t"		/*`参数1: 通道号，0对应console`*/
		"call tty_write\n\t"
		"addl $8,%%esp\n\t"	/*`丢弃前两个两个入栈参数`*/
		"popl %0\n\t"		/*`弹出第三个参数，长度值`*/
		"pop %%fs"
		::"r" (i):"ax","cx","dx");
	return i;
}
\end{lstlisting}
\chapter{格式化输出源码kernel/vsprintf.c}
vsprintf将可变数量的格式化参数列表，转换成字符串
\begin{lstlisting}[breaklines]
/* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */
/*
 * Wirzenius wrote this portably, Torvalds fucked it up :-)
 */

#include <stdarg.h>
#include <string.h>

/* we use this so that we can do without the ctype library */
#define is_digit(c)	((c) >= '0' && (c) <= '9')

static int skip_atoi(const char **s)
{
	int i=0;

	while (is_digit(**s))
		i = i*10 + *((*s)++) - '0';
	return i;
}

#define ZEROPAD	1		/* pad with zero */
#define SIGN	2		/* unsigned/signed long */
#define PLUS	4		/* show plus */
#define SPACE	8		/* space if plus */
#define LEFT	16		/* left justified */
#define SPECIAL	32		/* 0x */
#define SMALL	64		/* use 'abcdef' instead of 'ABCDEF' */

#define do_div(n,base) ({ \
int __res; \
__asm__("divl %4":"=a" (n),"=d" (__res):"0" (n),"1" (0),"r" (base)); \
__res; })
\end{lstlisting}
\section{number函数}
将数字转换成为指定进制的字符串；
\paragraph{输入} str 缓冲区指针；num 数字；base 进制；size 字符串长度；precision精度；type 类型；
\paragraph{输出} str 字符串指针；
\begin{lstlisting}[breaklines]
static char * number(char * str, int num, int base, int size, int precision
	,int type)
{
	char c,sign,tmp[36];
	const char *digits="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
	int i;

	if (type&SMALL) digits="0123456789abcdefghijklmnopqrstuvwxyz";
	if (type&LEFT) type &= ~ZEROPAD;
	if (base<2 || base>36)
		return 0;
	c = (type & ZEROPAD) ? '0' : ' ' ;
	if (type&SIGN && num<0) {
		sign='-';
		num = -num;
	} else
		sign=(type&PLUS) ? '+' : ((type&SPACE) ? ' ' : 0);
	if (sign) size--;
	if (type&SPECIAL)
		if (base==16) size -= 2;
		else if (base==8) size--;
	i=0;
	if (num==0)
		tmp[i++]='0';
	else while (num!=0)
		tmp[i++]=digits[do_div(num,base)];
	if (i>precision) precision=i;
	size -= precision;
	if (!(type&(ZEROPAD+LEFT)))
		while(size-->0)
			*str++ = ' ';
	if (sign)
		*str++ = sign;
	if (type&SPECIAL)
		if (base==8)
			*str++ = '0';
		else if (base==16) {
			*str++ = '0';
			*str++ = digits[33];
		}
	if (!(type&LEFT))
		while(size-->0)
			*str++ = c;
	while(i<precision--)
		*str++ = '0';
	while(i-->0)
		*str++ = tmp[i];
	while(size-->0)
		*str++ = ' ';
	return str;
}
\end{lstlisting}
\section{vsprintf函数}
\begin{lstlisting}[breaklines]
int vsprintf(char *buf, const char *fmt, va_list args)
{
	int len;
	int i;
	char * str;
	char *s;
	int *ip;

	int flags;		/* flags to number() */

	int field_width;	/* width of output field */
	int precision;		/* min. # of digits for integers; max
				   number of chars for from string */
	int qualifier;		/* 'h', 'l', or 'L' for integer fields */

	for (str=buf ; *fmt ; ++fmt) {
		if (*fmt != '%') {
			*str++ = *fmt;	/* `保存非格式控制字符` */
			continue;
		}
			
		/* process flags */
		flags = 0;
		repeat:		/* `\%后，1. 判断符号位` */
			++fmt;		/* this also skips first '%' */
			switch (*fmt) {
				case '-': flags |= LEFT; goto repeat;
				case '+': flags |= PLUS; goto repeat;
				case ' ': flags |= SPACE; goto repeat;
				case '#': flags |= SPECIAL; goto repeat;
				case '0': flags |= ZEROPAD; goto repeat;
				}
		
		/* get field width */
		field_width = -1;
		if (is_digit(*fmt))		/* `\%后，2. 判断宽度` */
			field_width = skip_atoi(&fmt);
		else if (*fmt == '*') {
			/* it's the next argument */
			field_width = va_arg(args, int);
			if (field_width < 0) {
				field_width = -field_width;
				flags |= LEFT;
			}
		}

		/* get the precision */
		precision = -1;
		if (*fmt == '.') {		/* `\%后，3. 判断精度` */
			++fmt;	
			if (is_digit(*fmt))
				precision = skip_atoi(&fmt);
			else if (*fmt == '*') {
				/* it's the next argument */
				precision = va_arg(args, int);
			}
			if (precision < 0)
				precision = 0;
		}

		/* get the conversion qualifier */
		qualifier = -1;			/* `\%后，4. 判断长度修饰符` */
		if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') {
			qualifier = *fmt;
			++fmt;
		}

		switch (*fmt) {
		case 'c':			/* `\%后，5. 判断是否字符` */
			if (!(flags & LEFT))
				while (--field_width > 0)
					*str++ = ' ';
			*str++ = (unsigned char) va_arg(args, int);	/* `保存输出字符` */
			while (--field_width > 0)
				*str++ = ' ';
			break;

		case 's':			/* `\%后，6. 判断是否字符串` */
			s = va_arg(args, char *);/* `获取并保存要输出的字符串` */
			len = strlen(s);
//			s=s-len;
			if (precision < 0)
				precision = len;
			else if (len > precision)
				len = precision;

			if (!(flags & LEFT))
				while (len < field_width--)
					*str++ = ' ';
			for (i = 0; i < len; ++i)
				*str++ = *s++;
			while (len < field_width--)
				*str++ = ' ';
			break;

		case 'o':			/* `\%后，7. 判断是否8进制数字` */
			str = number(str, va_arg(args, unsigned long), 8,
				field_width, precision, flags);
			break;

		case 'p':			/* `\%后，8. 判断是否指针` */
			if (field_width == -1) {
				field_width = 8;
				flags |= ZEROPAD;
			}
			str = number(str,
				(unsigned long) va_arg(args, void *), 16,
				field_width, precision, flags);
			break;

		case 'x':			/* `\%后，9. 判断是否16进制数字` */
			flags |= SMALL;
		case 'X':
			str = number(str, va_arg(args, unsigned long), 16,
				field_width, precision, flags);
			break;

		case 'd':
		case 'i':			/* `\%后，10. 判断是否10进制整数` */
			flags |= SIGN;
		case 'u':
			str = number(str, va_arg(args, unsigned long), 10,
				field_width, precision, flags);
			break;

		case 'n':			/* `\%后，11. 是否保存格式化字符长度` */
			ip = va_arg(args, int *);
			*ip = (str - buf);
			break;

		default:
			if (*fmt != '%')
				*str++ = '%';
			if (*fmt)			/* `\%后除\%不会再有其他情况了` */
				*str++ = *fmt;
			else
				--fmt;
			break;
		}
	}
	*str = '\0';
	return str-buf;
}
\end{lstlisting}
\chapter{内核调度源码kernel/sched.c}
此时我们并不需要调度的代码，只是把用户堆栈的定义放在了这里。
\begin{lstlisting}[breaklines]
#define PAGE_SIZE 4096

long user_stack [ PAGE_SIZE>>2 ] ;

struct {
        long * a;
        short b;
        } stack_start = { & user_stack [PAGE_SIZE>>2] , 0x10 };
\end{lstlisting}

\chapter{头文件include}
内核使用到的所有头文件都保存在include目录下，其中，和计算机体系结构密切相关的部分放在asm子目录下，和linux特有定义相关的放在linux子目录下，和内核资源相关的放在sys子目录下。
\section{include目录下的文件}
\section{stdarg.h}
定义了对标准的可变长参数处理，主要是va\_start、va\_end和va\_arg 三个宏定义。va\_start、va\_end 的使用可参考printk函数，printk函数接受可变长参数，在va\_start、va\_end 之间调用了vsprintf，将可变长参数转换为args传递给了vsprintf；在vsprintf中，使用va\_arg 逐次获得各个参数。
\begin{lstlisting}[breaklines]
#ifndef _STDARG_H
#define _STDARG_H

typedef char *va_list;

/* Amount of space required in an argument list for an arg of type TYPE.
   TYPE may alternatively be an expression whose type is used.  */

#define __va_rounded_size(TYPE)  \
  (((sizeof (TYPE) + sizeof (int) - 1) / sizeof (int)) * sizeof (int))

#ifndef __sparc__
#define va_start(AP, LASTARG) 						\
 (AP = ((char *) &(LASTARG) + __va_rounded_size (LASTARG)))
#else
#define va_start(AP, LASTARG) 						\
 (__builtin_saveregs (),						\
  AP = ((char *) &(LASTARG) + __va_rounded_size (LASTARG)))
#endif

void va_end (va_list);		/* Defined in gnulib */
#define va_end(AP)

#define va_arg(AP, TYPE)						\
 (AP += __va_rounded_size (TYPE),					\
  *((TYPE *) (AP - __va_rounded_size (TYPE))))

#endif /* _STDARG_H */
\end{lstlisting}
\section{stddef.h}
定义了可在独立环境中使用宏和类型。
\begin{lstlisting}[breaklines]
#ifndef _STDDEF_H
#define _STDDEF_H

#ifndef _PTRDIFF_T
#define _PTRDIFF_T
typedef long ptrdiff_t;
#endif

#ifndef _SIZE_T
#define _SIZE_T
typedef unsigned long size_t;
#endif

#undef NULL
#define NULL ((void *)0)

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

#endif
\end{lstlisting}
\section{string.h}
以内嵌函数的形式定义了所有的字符串操作函数。注意这里对strlen函数做了修改，在仿真中发现在进入strlen时，字符串指针被放入到edi中，而strlen中为了计算lenth，移动了edi中的字符串指针，造成strlen函数结束时，edi中的字符串指针已经被指向他处，其他字符串处理函数是否也会出现这样的问题尚未可知。
\begin{lstlisting}[breaklines]
#ifndef _STRING_H_
#define _STRING_H_

#ifndef NULL
#define NULL ((void *) 0)
#endif

#ifndef _SIZE_T
#define _SIZE_T
typedef unsigned int size_t;
#endif

extern char * strerror(int errno);

/*
 * This string-include defines all string functions as inline
 * functions. Use gcc. It also assumes ds=es=data space, this should be
 * normal. Most of the string-functions are rather heavily hand-optimized,
 * see especially strtok,strstr,str[c]spn. They should work, but are not
 * very easy to understand. Everything is done entirely within the register
 * set, making the functions fast and clean. String instructions have been
 * used through-out, making for "slightly" unclear code :-)
 *
 *		(C) 1991 Linus Torvalds
 */
 
static inline char * strcpy(char * dest,const char *src)
{
__asm__("cld\n"
	"1:\tlodsb\n\t"
	"stosb\n\t"
	"testb %%al,%%al\n\t"
	"jne 1b"
	::"S" (src),"D" (dest));
return dest;
}

static inline char * strncpy(char * dest,const char *src,int count)
{
__asm__("cld\n"
	"1:\tdecl %2\n\t"
	"js 2f\n\t"
	"lodsb\n\t"
	"stosb\n\t"
	"testb %%al,%%al\n\t"
	"jne 1b\n\t"
	"rep\n\t"
	"stosb\n"
	"2:"
	::"S" (src),"D" (dest),"c" (count));
return dest;
}

static inline char * strcat(char * dest,const char * src)
{
__asm__("cld\n\t"
	"repne\n\t"
	"scasb\n\t"
	"decl %1\n"
	"1:\tlodsb\n\t"
	"stosb\n\t"
	"testb %%al,%%al\n\t"
	"jne 1b"
	::"S" (src),"D" (dest),"a" (0),"c" (0xffffffff));
return dest;
}

static inline char * strncat(char * dest,const char * src,int count)
{
__asm__("cld\n\t"
	"repne\n\t"
	"scasb\n\t"
	"decl %1\n\t"
	"movl %4,%3\n"
	"1:\tdecl %3\n\t"
	"js 2f\n\t"
	"lodsb\n\t"
	"stosb\n\t"
	"testb %%al,%%al\n\t"
	"jne 1b\n"
	"2:\txorl %2,%2\n\t"
	"stosb"
	::"S" (src),"D" (dest),"a" (0),"c" (0xffffffff),"g" (count)
	);
return dest;
}

static inline int strcmp(const char * cs,const char * ct)
{
register int __res __asm__("ax");
__asm__("cld\n"
	"1:\tlodsb\n\t"
	"scasb\n\t"
	"jne 2f\n\t"
	"testb %%al,%%al\n\t"
	"jne 1b\n\t"
	"xorl %%eax,%%eax\n\t"
	"jmp 3f\n"
	"2:\tmovl $1,%%eax\n\t"
	"jl 3f\n\t"
	"negl %%eax\n"
	"3:"
	:"=a" (__res):"D" (cs),"S" (ct));
return __res;
}

static inline int strncmp(const char * cs,const char * ct,int count)
{
register int __res __asm__("ax");
__asm__("cld\n"
	"1:\tdecl %3\n\t"
	"js 2f\n\t"
	"lodsb\n\t"
	"scasb\n\t"
	"jne 3f\n\t"
	"testb %%al,%%al\n\t"
	"jne 1b\n"
	"2:\txorl %%eax,%%eax\n\t"
	"jmp 4f\n"
	"3:\tmovl $1,%%eax\n\t"
	"jl 4f\n\t"
	"negl %%eax\n"
	"4:"
	:"=a" (__res):"D" (cs),"S" (ct),"c" (count));
return __res;
}

static inline char * strchr(const char * s,char c)
{
register char * __res __asm__("ax");
__asm__("cld\n\t"
	"movb %%al,%%ah\n"
	"1:\tlodsb\n\t"
	"cmpb %%ah,%%al\n\t"
	"je 2f\n\t"
	"testb %%al,%%al\n\t"
	"jne 1b\n\t"
	"movl $1,%1\n"
	"2:\tmovl %1,%0\n\t"
	"decl %0"
	:"=a" (__res):"S" (s),"0" (c));
return __res;
}

static inline char * strrchr(const char * s,char c)
{
register char * __res __asm__("dx");
__asm__("cld\n\t"
	"movb %%al,%%ah\n"
	"1:\tlodsb\n\t"
	"cmpb %%ah,%%al\n\t"
	"jne 2f\n\t"
	"movl %%esi,%0\n\t"
	"decl %0\n"
	"2:\ttestb %%al,%%al\n\t"
	"jne 1b"
	:"=d" (__res):"0" (0),"S" (s),"a" (c));
return __res;
}

static inline int strspn(const char * cs, const char * ct)
{
register char * __res __asm__("si");
__asm__("cld\n\t"
	"movl %4,%%edi\n\t"
	"repne\n\t"
	"scasb\n\t"
	"notl %%ecx\n\t"
	"decl %%ecx\n\t"
	"movl %%ecx,%%edx\n"
	"1:\tlodsb\n\t"
	"testb %%al,%%al\n\t"
	"je 2f\n\t"
	"movl %4,%%edi\n\t"
	"movl %%edx,%%ecx\n\t"
	"repne\n\t"
	"scasb\n\t"
	"je 1b\n"
	"2:\tdecl %0"
	:"=S" (__res):"a" (0),"c" (0xffffffff),"0" (cs),"g" (ct)
	);
return __res-cs;
}

static inline int strcspn(const char * cs, const char * ct)
{
register char * __res __asm__("si");
__asm__("cld\n\t"
	"movl %4,%%edi\n\t"
	"repne\n\t"
	"scasb\n\t"
	"notl %%ecx\n\t"
	"decl %%ecx\n\t"
	"movl %%ecx,%%edx\n"
	"1:\tlodsb\n\t"
	"testb %%al,%%al\n\t"
	"je 2f\n\t"
	"movl %4,%%edi\n\t"
	"movl %%edx,%%ecx\n\t"
	"repne\n\t"
	"scasb\n\t"
	"jne 1b\n"
	"2:\tdecl %0"
	:"=S" (__res):"a" (0),"c" (0xffffffff),"0" (cs),"g" (ct)
	);
return __res-cs;
}

static inline char * strpbrk(const char * cs,const char * ct)
{
register char * __res __asm__("si");
__asm__("cld\n\t"
	"movl %4,%%edi\n\t"
	"repne\n\t"
	"scasb\n\t"
	"notl %%ecx\n\t"
	"decl %%ecx\n\t"
	"movl %%ecx,%%edx\n"
	"1:\tlodsb\n\t"
	"testb %%al,%%al\n\t"
	"je 2f\n\t"
	"movl %4,%%edi\n\t"
	"movl %%edx,%%ecx\n\t"
	"repne\n\t"
	"scasb\n\t"
	"jne 1b\n\t"
	"decl %0\n\t"
	"jmp 3f\n"
	"2:\txorl %0,%0\n"
	"3:"
	:"=S" (__res):"a" (0),"c" (0xffffffff),"0" (cs),"g" (ct)
	);
return __res;
}

static inline char * strstr(const char * cs,const char * ct)
{
register char * __res __asm__("ax");
__asm__("cld\n\t" \
	"movl %4,%%edi\n\t"
	"repne\n\t"
	"scasb\n\t"
	"notl %%ecx\n\t"
	"decl %%ecx\n\t"	/* NOTE! This also sets Z if searchstring='' */
	"movl %%ecx,%%edx\n"
	"1:\tmovl %4,%%edi\n\t"
	"movl %%esi,%%eax\n\t"
	"movl %%edx,%%ecx\n\t"
	"repe\n\t"
	"cmpsb\n\t"
	"je 2f\n\t"		/* also works for empty string, see above */
	"xchgl %%eax,%%esi\n\t"
	"incl %%esi\n\t"
	"cmpb $0,-1(%%eax)\n\t"
	"jne 1b\n\t"
	"xorl %%eax,%%eax\n\t"
	"2:"
	:"=a" (__res):"0" (0),"c" (0xffffffff),"S" (cs),"g" (ct)
	);
return __res;
}

static inline int strlen(const char * s)
{
register int __res __asm__("cx");
__asm__("push %%edi\n\t"
	"cld\n\t"
	"repne\n\t"
	"scasb\n\t"
	"notl %0\n\t"
	"decl %0\n\t"
	"pop %%edi"
	:"=c" (__res):"D" (s),"a" (0),"0" (0xffffffff));
return __res;
}

extern char * ___strtok;

static inline char * strtok(char * s,const char * ct)
{
register char * __res __asm__("si");
__asm__("testl %1,%1\n\t"
	"jne 1f\n\t"
	"testl %0,%0\n\t"
	"je 8f\n\t"
	"movl %0,%1\n"
	"1:\txorl %0,%0\n\t"
	"movl $-1,%%ecx\n\t"
	"xorl %%eax,%%eax\n\t"
	"cld\n\t"
	"movl %4,%%edi\n\t"
	"repne\n\t"
	"scasb\n\t"
	"notl %%ecx\n\t"
	"decl %%ecx\n\t"
	"je 7f\n\t"			/* empty delimeter-string */
	"movl %%ecx,%%edx\n"
	"2:\tlodsb\n\t"
	"testb %%al,%%al\n\t"
	"je 7f\n\t"
	"movl %4,%%edi\n\t"
	"movl %%edx,%%ecx\n\t"
	"repne\n\t"
	"scasb\n\t"
	"je 2b\n\t"
	"decl %1\n\t"
	"cmpb $0,(%1)\n\t"
	"je 7f\n\t"
	"movl %1,%0\n"
	"3:\tlodsb\n\t"
	"testb %%al,%%al\n\t"
	"je 5f\n\t"
	"movl %4,%%edi\n\t"
	"movl %%edx,%%ecx\n\t"
	"repne\n\t"
	"scasb\n\t"
	"jne 3b\n\t"
	"decl %1\n\t"
	"cmpb $0,(%1)\n\t"
	"je 5f\n\t"
	"movb $0,(%1)\n\t"
	"incl %1\n\t"
	"jmp 6f\n"
	"5:\txorl %1,%1\n"
	"6:\tcmpb $0,(%0)\n\t"
	"jne 7f\n\t"
	"xorl %0,%0\n"
	"7:\ttestl %0,%0\n\t"
	"jne 8f\n\t"
	"movl %0,%1\n"
	"8:"
	:"=b" (__res),"=S" (___strtok)
	:"0" (___strtok),"1" (s),"g" (ct)
	);
return __res;
}

static inline void * memcpy(void * dest,const void * src, int n)
{
__asm__("cld\n\t"
	"rep\n\t"
	"movsb"
	::"c" (n),"S" (src),"D" (dest)
	);
return dest;
}

static inline void * memmove(void * dest,const void * src, int n)
{
if (dest<src)
__asm__("cld\n\t"
	"rep\n\t"
	"movsb"
	::"c" (n),"S" (src),"D" (dest)
	);
else
__asm__("std\n\t"
	"rep\n\t"
	"movsb"
	::"c" (n),"S" (src+n-1),"D" (dest+n-1)
	);
return dest;
}

static inline int memcmp(const void * cs,const void * ct,int count)
{
register int __res __asm__("ax");
__asm__("cld\n\t"
	"repe\n\t"
	"cmpsb\n\t"
	"je 1f\n\t"
	"movl $1,%%eax\n\t"
	"jl 1f\n\t"
	"negl %%eax\n"
	"1:"
	:"=a" (__res):"0" (0),"D" (cs),"S" (ct),"c" (count)
	);
return __res;
}

static inline void * memchr(const void * cs,char c,int count)
{
register void * __res __asm__("di");
if (!count)
	return NULL;
__asm__("cld\n\t"
	"repne\n\t"
	"scasb\n\t"
	"je 1f\n\t"
	"movl $1,%0\n"
	"1:\tdecl %0"
	:"=D" (__res):"a" (c),"D" (cs),"c" (count)
	);
return __res;
}

static inline void * memset(void * s,char c,int count)
{
__asm__("cld\n\t"
	"rep\n\t"
	"stosb"
	::"a" (c),"D" (s),"c" (count)
	);
return s;
}

#endif
\end{lstlisting}
\section{termios.h}
包含终端io接口定义。原文中有termio和termios，后者符合posix标准，我们只使用后者。
\begin{lstlisting}[breaklines]
#ifndef _TERMIOS_H
#define _TERMIOS_H

#define TTY_BUF_SIZE 1024

/* 0x54 is just a magic number to make these relatively uniqe ('T') */

#define TCGETS		0x5401
#define TCSETS		0x5402
#define TCSETSW		0x5403
#define TCSETSF		0x5404
#define TCGETA		0x5405
#define TCSETA		0x5406
#define TCSETAW		0x5407
#define TCSETAF		0x5408
#define TCSBRK		0x5409
#define TCXONC		0x540A
#define TCFLSH		0x540B
#define TIOCEXCL	0x540C
#define TIOCNXCL	0x540D
#define TIOCSCTTY	0x540E
#define TIOCGPGRP	0x540F
#define TIOCSPGRP	0x5410
#define TIOCOUTQ	0x5411
#define TIOCSTI		0x5412
#define TIOCGWINSZ	0x5413
#define TIOCSWINSZ	0x5414
#define TIOCMGET	0x5415
#define TIOCMBIS	0x5416
#define TIOCMBIC	0x5417
#define TIOCMSET	0x5418
#define TIOCGSOFTCAR	0x5419
#define TIOCSSOFTCAR	0x541A

struct winsize {
	unsigned short ws_row;
	unsigned short ws_col;
	unsigned short ws_xpixel;
	unsigned short ws_ypixel;
};

#define NCC 8
struct termio {
	unsigned short c_iflag;		/* input mode flags */
	unsigned short c_oflag;		/* output mode flags */
	unsigned short c_cflag;		/* control mode flags */
	unsigned short c_lflag;		/* local mode flags */
	unsigned char c_line;		/* line discipline */
	unsigned char c_cc[NCC];	/* control characters */
};

#define NCCS 17
struct termios {
	unsigned long c_iflag;		/* input mode flags */
	unsigned long c_oflag;		/* output mode flags */
	unsigned long c_cflag;		/* control mode flags */
	unsigned long c_lflag;		/* local mode flags */
	unsigned char c_line;		/* line discipline */
	unsigned char c_cc[NCCS];	/* control characters */
};

/* c_cc characters */
#define VINTR 0
#define VQUIT 1
#define VERASE 2
#define VKILL 3
#define VEOF 4
#define VTIME 5
#define VMIN 6
#define VSWTC 7
#define VSTART 8
#define VSTOP 9
#define VSUSP 10
#define VEOL 11
#define VREPRINT 12
#define VDISCARD 13
#define VWERASE 14
#define VLNEXT 15
#define VEOL2 16

/* c_iflag bits */
#define IGNBRK	0000001
#define BRKINT	0000002
#define IGNPAR	0000004
#define PARMRK	0000010
#define INPCK	0000020
#define ISTRIP	0000040
#define INLCR	0000100
#define IGNCR	0000200
#define ICRNL	0000400
#define IUCLC	0001000
#define IXON	0002000
#define IXANY	0004000
#define IXOFF	0010000
#define IMAXBEL	0020000

/* c_oflag bits */
#define OPOST	0000001
#define OLCUC	0000002
#define ONLCR	0000004
#define OCRNL	0000010
#define ONOCR	0000020
#define ONLRET	0000040
#define OFILL	0000100
#define OFDEL	0000200
#define NLDLY	0000400
#define   NL0	0000000
#define   NL1	0000400
#define CRDLY	0003000
#define   CR0	0000000
#define   CR1	0001000
#define   CR2	0002000
#define   CR3	0003000
#define TABDLY	0014000
#define   TAB0	0000000
#define   TAB1	0004000
#define   TAB2	0010000
#define   TAB3	0014000
#define   XTABS	0014000
#define BSDLY	0020000
#define   BS0	0000000
#define   BS1	0020000
#define VTDLY	0040000
#define   VT0	0000000
#define   VT1	0040000
#define FFDLY	0040000
#define   FF0	0000000
#define   FF1	0040000

/* c_cflag bit meaning */
#define CBAUD	0000017
#define  B0	0000000		/* hang up */
#define  B50	0000001
#define  B75	0000002
#define  B110	0000003
#define  B134	0000004
#define  B150	0000005
#define  B200	0000006
#define  B300	0000007
#define  B600	0000010
#define  B1200	0000011
#define  B1800	0000012
#define  B2400	0000013
#define  B4800	0000014
#define  B9600	0000015
#define  B19200	0000016
#define  B38400	0000017
#define CSIZE	0000060
#define   CS5	0000000
#define   CS6	0000020
#define   CS7	0000040
#define   CS8	0000060
#define CSTOPB	0000100
#define CREAD	0000200
#define CPARENB	0000400
#define CPARODD	0001000
#define HUPCL	0002000
#define CLOCAL	0004000
#define CIBAUD	03600000		/* input baud rate (not used) */
#define CRTSCTS	020000000000		/* flow control */

/* c_lflag bits */
#define ISIG	0000001
#define ICANON	0000002
#define XCASE	0000004
#define ECHO	0000010
#define ECHOE	0000020
#define ECHOK	0000040
#define ECHONL	0000100
#define NOFLSH	0000200
#define TOSTOP	0000400
#define ECHOCTL	0001000
#define ECHOPRT	0002000
#define ECHOKE	0004000
#define FLUSHO	0010000
#define PENDIN	0040000
#define IEXTEN	0100000

/* modem lines */
#define TIOCM_LE	0x001
#define TIOCM_DTR	0x002
#define TIOCM_RTS	0x004
#define TIOCM_ST	0x008
#define TIOCM_SR	0x010
#define TIOCM_CTS	0x020
#define TIOCM_CAR	0x040
#define TIOCM_RNG	0x080
#define TIOCM_DSR	0x100
#define TIOCM_CD	TIOCM_CAR
#define TIOCM_RI	TIOCM_RNG

/* tcflow() and TCXONC use these */
#define	TCOOFF		0
#define	TCOON		1
#define	TCIOFF		2
#define	TCION		3

/* tcflush() and TCFLSH use these */
#define	TCIFLUSH	0
#define	TCOFLUSH	1
#define	TCIOFLUSH	2

/* tcsetattr uses these */
#define	TCSANOW		0
#define	TCSADRAIN	1
#define	TCSAFLUSH	2

typedef int speed_t;

extern speed_t cfgetispeed(struct termios *termios_p);
extern speed_t cfgetospeed(struct termios *termios_p);
extern int cfsetispeed(struct termios *termios_p, speed_t speed);
extern int cfsetospeed(struct termios *termios_p, speed_t speed);
extern int tcdrain(int fildes);
extern int tcflow(int fildes, int action);
extern int tcflush(int fildes, int queue_selector);
extern int tcgetattr(int fildes, struct termios *termios_p);
extern int tcsendbreak(int fildes, int duration);
extern int tcsetattr(int fildes, int optional_actions,
	struct termios *termios_p);

#endif
\end{lstlisting}
\section{include/asm目录下的文件}
\section{asm/io.h}
定义了硬件io端口的操作，包括了含延迟和不含延迟的两种方式；
\begin{lstlisting}[breaklines]
#define outb(value,port) \
__asm__ ("outb %%al,%%dx"::"a" (value),"d" (port))


#define inb(port) ({ \
unsigned char _v; \
__asm__ volatile ("inb %%dx,%%al":"=a" (_v):"d" (port)); \
_v; \
})

#define outb_p(value,port) \
__asm__ ("outb %%al,%%dx\n" \
		"\tjmp 1f\n" \
		"1:\tjmp 1f\n" \
		"1:"::"a" (value),"d" (port))

#define inb_p(port) ({ \
unsigned char _v; \
__asm__ volatile ("inb %%dx,%%al\n" \
	"\tjmp 1f\n" \
	"1:\tjmp 1f\n" \
	"1:":"=a" (_v):"d" (port)); \
_v; \
})
\end{lstlisting}
\section{asm/segment.h}
定义了访问段寄存器的相关操作；
\begin{lstlisting}[breaklines]
static inline unsigned char get_fs_byte(const char * addr)
{
	unsigned register char _v;

	__asm__ ("movb %%fs:%1,%0":"=r" (_v):"m" (*addr));
	return _v;
}

static inline unsigned short get_fs_word(const unsigned short *addr)
{
	unsigned short _v;

	__asm__ ("movw %%fs:%1,%0":"=r" (_v):"m" (*addr));
	return _v;
}

static inline unsigned long get_fs_long(const unsigned long *addr)
{
	unsigned long _v;

	__asm__ ("movl %%fs:%1,%0":"=r" (_v):"m" (*addr)); \
	return _v;
}

static inline void put_fs_byte(char val,char *addr)
{
__asm__ ("movb %0,%%fs:%1"::"r" (val),"m" (*addr));
}

static inline void put_fs_word(short val,short * addr)
{
__asm__ ("movw %0,%%fs:%1"::"r" (val),"m" (*addr));
}

static inline void put_fs_long(unsigned long val,unsigned long * addr)
{
__asm__ ("movl %0,%%fs:%1"::"r" (val),"m" (*addr));
}
\end{lstlisting}
\section{asm/system.h}
定义开关中断，设置中断向量表等系统级操作；
\begin{lstlisting} [breaklines]
#define move_to_user_mode() \
__asm__ ("movl %%esp,%%eax\n\t" \
	"pushl $0x17\n\t" \
	"pushl %%eax\n\t" \
	"pushfl\n\t" \
	"pushl $0x0f\n\t" \
	"pushl $1f\n\t" \
	"iret\n" \
	"1:\tmovl $0x17,%%eax\n\t" \
	"movw %%ax,%%ds\n\t" \
	"movw %%ax,%%es\n\t" \
	"movw %%ax,%%fs\n\t" \
	"movw %%ax,%%gs" \
	:::"ax")

#define sti() __asm__ ("sti"::)
#define cli() __asm__ ("cli"::)
#define nop() __asm__ ("nop"::)

#define iret() __asm__ ("iret"::)

#define _set_gate(gate_addr,type,dpl,addr) \
__asm__ ("movw %%dx,%%ax\n\t" \
	"movw %0,%%dx\n\t" \
	"movl %%eax,%1\n\t" \
	"movl %%edx,%2" \
	: \
	: "i" ((short) (0x8000+(dpl<<13)+(type<<8))), \
	"o" (*((char *) (gate_addr))), \
	"o" (*(4+(char *) (gate_addr))), \
	"d" ((char *) (addr)),"a" (0x00080000))

#define set_intr_gate(n,addr) \
	_set_gate(&idt[n],14,0,addr)

#define set_trap_gate(n,addr) \
	_set_gate(&idt[n],15,0,addr)

#define set_system_gate(n,addr) \
	_set_gate(&idt[n],15,3,addr)

#define _set_seg_desc(gate_addr,type,dpl,base,limit) {\
	*(gate_addr) = ((base) & 0xff000000) | \
		(((base) & 0x00ff0000)>>16) | \
		((limit) & 0xf0000) | \
		((dpl)<<13) | \
		(0x00408000) | \
		((type)<<8); \
	*((gate_addr)+1) = (((base) & 0x0000ffff)<<16) | \
		((limit) & 0x0ffff); }

#define _set_tssldt_desc(n,addr,type) \
__asm__ ("movw $104,%1\n\t" \
	"movw %%ax,%2\n\t" \
	"rorl $16,%%eax\n\t" \
	"movb %%al,%3\n\t" \
	"movb $" type ",%4\n\t" \
	"movb $0x00,%5\n\t" \
	"movb %%ah,%6\n\t" \
	"rorl $16,%%eax" \
	::"a" (addr), "m" (*(n)), "m" (*(n+2)), "m" (*(n+4)), \
	 "m" (*(n+5)), "m" (*(n+6)), "m" (*(n+7)) \
	)

#define set_tss_desc(n,addr) _set_tssldt_desc(((char *) (n)),addr,"0x89")
#define set_ldt_desc(n,addr) _set_tssldt_desc(((char *) (n)),addr,"0x82")
\end{lstlisting}
\section{include/linux目录下的文件}
\section{linux/tty.h}
字符终端的数据结构定义，这里的proc\_list是用queue的进程列表，我们还讨论不到进程，先用void类型替代；
\begin{lstlisting}[breaklines]
/*
 * 'tty.h' defines some structures used by tty_io.c and some defines.
 *
 * NOTE! Don't touch this without checking that nothing in rs_io.s or
 * con_io.s breaks. Some constants are hardwired into the system (mainly
 * offsets into 'tty_queue'
 */

#ifndef _TTY_H
#define _TTY_H

#include <termios.h>

#define TTY_BUF_SIZE 1024

struct tty_queue {
	unsigned long data;
	unsigned long head;
	unsigned long tail;
//	struct task_struct * proc_list;
	void * proc_list;
	char buf[TTY_BUF_SIZE];
};

#define INC(a) ((a) = ((a)+1) & (TTY_BUF_SIZE-1))
#define DEC(a) ((a) = ((a)-1) & (TTY_BUF_SIZE-1))
#define EMPTY(a) ((a).head == (a).tail)
#define LEFT(a) (((a).tail-(a).head-1)&(TTY_BUF_SIZE-1))
#define LAST(a) ((a).buf[(TTY_BUF_SIZE-1)&((a).head-1)])
#define FULL(a) (!LEFT(a))
#define CHARS(a) (((a).head-(a).tail)&(TTY_BUF_SIZE-1))
#define GETCH(queue,c) \
(void)({c=(queue).buf[(queue).tail];INC((queue).tail);})
#define PUTCH(c,queue) \
(void)({(queue).buf[(queue).head]=(c);INC((queue).head);})

#define EOF_CHAR(tty) ((tty)->termios.c_cc[VEOF])
#define INTR_CHAR(tty) ((tty)->termios.c_cc[VINTR])
#define STOP_CHAR(tty) ((tty)->termios.c_cc[VSTOP])
#define START_CHAR(tty) ((tty)->termios.c_cc[VSTART])
#define ERASE_CHAR(tty) ((tty)->termios.c_cc[VERASE])

struct tty_struct {
	struct termios termios;
	int pgrp;
	int stopped;
	void (*write)(struct tty_struct * tty);
	struct tty_queue read_q;
	struct tty_queue write_q;
	struct tty_queue secondary;
	};

extern struct tty_struct tty_table[];

/*	intr=^C		quit=^|		erase=del	kill=^U
	eof=^D		vtime=\0	vmin=\1		sxtc=\0
	start=^Q	stop=^S		susp=^Y		eol=\0
	reprint=^R	discard=^U	werase=^W	lnext=^V
	eol2=\0
*/
#define INIT_C_CC "\003\034\177\025\004\0\1\0\021\023\031\0\022\017\027\026\0"

void con_init(void);
void tty_init(void);

//int tty_read(unsigned c, char * buf, int n);
//int tty_write(unsigned c, char * buf, int n);
void con_write(struct tty_struct * tty);
#endif
\end{lstlisting}
\section{include/sys目录下的文件}
\section{sys/types.h}
系统使用的基本数据类型；和stddef里面的不同，这些类型可以看作是linux专用的。
\begin{lstlisting} [breaklines]
#ifndef _SYS_TYPES_H
#define _SYS_TYPES_H

#ifndef _SIZE_T
#define _SIZE_T
typedef unsigned int size_t;
#endif

#ifndef _TIME_T
#define _TIME_T
typedef long time_t;
#endif

#ifndef _PTRDIFF_T
#define _PTRDIFF_T
typedef long ptrdiff_t;
#endif

#ifndef NULL
#define NULL ((void *) 0)
#endif

typedef int pid_t;
typedef unsigned short uid_t;
typedef unsigned char gid_t;
typedef unsigned short dev_t;
typedef unsigned short ino_t;
typedef unsigned short mode_t;
typedef unsigned short umode_t;
typedef unsigned char nlink_t;
typedef int daddr_t;
typedef long off_t;
typedef unsigned char u_char;
typedef unsigned short ushort;

typedef struct { int quot,rem; } div_t;
typedef struct { long quot,rem; } ldiv_t;

struct ustat {
	daddr_t f_tfree;
	ino_t f_tinode;
	char f_fname[6];
	char f_fpack[6];
};

#endif
\end{lstlisting}
\chapter{工程文件}
\section{Makefile}
相对001, 增加了kernel目录，Makefile需要增加到kernel目录下的Makefile的调用；
\begin{lstlisting} [breaklines]
AS86    =as86 -0 -a
LD86    =ld86 -0

AS      =as --32
LD      =ld -melf_i386 -e startup_32 -Ttext 0
LDFLAGS =-s -x -M
CC      =gcc -m32
CFLAGS  =-Wall -O -fstrength-reduce -fomit-frame-pointer
CPP     =gcc -E -nostdinc -Iinclude

ARCHIVES=kernel/kernel.o
#mm/mm.o fs/fs.o
#LIBS   =lib/lib.a

.c.s:
        $(CC) $(CFLAGS) \
        -nostdinc -Iinclude -S -o $*.s $<
.s.o:
        $(AS) -c -o $*.o $<
.c.o:
        $(CC) $(CFLAGS) \
        -nostdinc -Iinclude -c -o $*.o $<

all:    Image

Image: boot/boot tools/system tools/build
        tools/build boot/boot tools/system > Image
        sync

tools/build: tools/build.c
        $(CC) $(CFLAGS) \
        -o tools/build tools/build.c

boot/head.o: boot/head.s

tools/system:   boot/head.o init/main.o \
                $(ARCHIVES) $(LIBS)
        $(LD) $(LDFLAGS) boot/head.o init/main.o \
                $(ARCHIVES) \
        -o tools/system > System.map

kernel/kernel.o:
        (cd kernel; make)

#mm/mm.o:
#       (cd mm; make)

#fs/fs.o:
#       (cd fs; make)

#lib/lib.a:
#       (cd lib; make)

boot/boot:      boot/boot.s tools/system
#       (echo -n "SYSSIZE = (";ls -l tools/system | grep system \
#               | cut -c25-31 | tr '\012' ' '; echo "+ 15 ) / 16") > tmp.s
#       cat boot/boot.s >> tmp.s
        (./tmp.sh)
        $(AS86) -o boot/boot.o tmp.s
        rm -f tmp.s
        $(LD86) -s -o boot/boot boot/boot.o

clean:
        rm -f Image System.map tmp_make boot/boot core
        rm -f init/*.o boot/*.o tools/system tools/build
#       (cd mm;make clean)
#       (cd fs;make clean)
        (cd kernel;make clean)
#       (cd lib;make clean)

backup: clean
        (cd .. ; tar cf - linux | compress16 - > backup.Z)
        sync

dep:
        sed '/\#\#\# Dependencies/q' < Makefile > tmp_make
        (for i in init/*.c;do echo -n "init/";$(CPP) -M $$i;done) >> tmp_make
        cp tmp_make Makefile
#       (cd fs; make dep)
#       (cd kernel; make dep)
#       (cd mm; make dep)

### Dependencies:
init/main.o: init/main.c include/linux/tty.h include/termios.h
\end{lstlisting}
\section{kernel/Makefile}
\paragraph{1}kernel目录下的Makefile和主Makefile类似，不再赘述；
\begin{lstlisting}

# Makefile for the FREAX-kernel.
#
# Note! Dependencies are done automagically by 'make dep', which also
# removes any old dependencies. DON'T put your own dependencies here
# unless it's something special (ie not a .c file).
#

AR      =gar
AS      =as --32
LD      =ld -melf_i386
LDFLAGS =-s -x
CC      =gcc -m32
CFLAGS  =-w -O -fstrength-reduce -fomit-frame-pointer \
        -finline-functions -nostdinc -I../include -fno-stack-protector
CPP     =gcc -E -nostdinc -I../include

.c.s:
        $(CC) $(CFLAGS) \
        -S -o $*.s $<
.s.o:
        $(AS) -c -o $*.o $<
.c.o:
        $(CC) $(CFLAGS) \
        -c -o $*.o $<

OBJS  = sched.o console.o tty_io.o printk.o vsprintf.o
#       system_call.o traps.o asm.o fork.o \
        panic.o mktime.o \
        keyboard.o rs_io.o hd.o sys.o exit.o serial.o

kernel.o: $(OBJS)
        $(LD) -r -o kernel.o $(OBJS)
        sync

clean:
        rm -f core *.o *.a tmp_make
        for i in *.c;do rm -f ``basename $$i .c``.s;done      

dep:
        sed '/\#\#\# Dependencies/q' < Makefile > tmp_make
        (for i in *.c;do echo -n ``echo $$i | sed 's,\.c,\.s,'``" "; \
                $(CPP) -M $$i;done) >> tmp_make
        cp tmp_make Makefile
### Dependencies:
console.s console.o: console.c ../include/asm/io.h ../include/asm/system.h \
 ../include/linux/tty.h ../include/termios.h
printk.s printk.o: printk.c ../include/stdarg.h ../include/stddef.h
sched.s sched.o: sched.c
tty_io.s tty_io.o: tty_io.c ../include/linux/tty.h ../include/termios.h
clean:
        rm -f core *.o *.a tmp_make
        for i in *.c;do rm -f ``basename $$i .c``.s;done
\end{lstlisting}
\chapter{模拟运行}
\section{从符号文件中找到main函数的入口地址}
使用文本编辑器打开System.map文件，可以使用查找文本的方式找到main函数的入口地址
\begin{lstlisting}[breaklines]
	...
 .text          0x0000000000005078      0x250 init/main.o
                0x0000000000005078                fork
                0x00000000000050a8                pause
                0x00000000000050d8                init
                0x0000000000005102                main
	...                
\end{lstlisting}
\section{从反汇编文件找到main函数}
\begin{lstlisting}[breaklines]
objdump tools/system -dSsx > system.s
`使用文本编辑器打开system.s，下拉到5102部分，即可看到main函数的反汇编：`
......
    5102:	f3 0f 1e fb          	endbr32 
    5106:	8d 4c 24 04          	lea    0x4(%esp),%ecx
    510a:	83 e4 f0             	and    $0xfffffff0,%esp
    510d:	ff 71 fc             	pushl  -0x4(%ecx)
    5110:	55                   	push   %ebp
    5111:	89 e5                	mov    %esp,%ebp
    5113:	57                   	push   %edi
......
\end{lstlisting}

\end{CJK}
\end{document}
